package com.dirven.game;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.badlogic.gdx.Gdx;
import com.dirven.game.sprite.GridSprite;

public class Path {
	
	// An arraylist of waypoints
	private ArrayList<Coord> waypoints;
	
	// Constant settings
	final static boolean REVERSE_DIRECTION = true;
	final static boolean NO_REVERSE_DIRECTION = false;
	
	// Reverse direction setting. When set to true, the NPC will reverse direction and follow the
	// waypoints backwards. When set to false, the NPC will not reverse direction, and would rather
	// stand in the ending waypoint
	boolean reverseDirection;
	
	// The previous waypoint index
	int previousWP = -1;
	// The next waypoint index.
	int nextWP = previousWP;
	
	// When set to true, the npc moves toward the last waypoint from the first.
	// WHen set to false, the npc moves toward the first waypoint from the last.
	boolean directionForward = true;
	
	Coord nextWPCoordinate = null;
	
	GridSprite sprite;
	
	private boolean isFinishedMovingAlongPath = false;
	
	/**
	 * Method Waypoint
	 *
	 *
	 */
	public Path(GridSprite gs, boolean setting) {
		this.reverseDirection = setting;
		this.sprite = gs;
		waypoints = new ArrayList<Coord>();
	}
	
	/**
	 * Add a waypoint to the path.
	 */
	public void addWaypoint(int x, int y) {
		addWaypoint(new Coord((float)x, (float)y) );
	}
	
	/**
	 * Add a waypoint to the path.
	 */
	public void addWaypoint(Coord newWP) {
		// Make sure new waypoint isn't on an occupied space.
		if(Grid.collide(newWP) == false) {
			waypoints.add( newWP );
		}
	}
	
	/**
	 * Add an array of waypoints to the path.
	 */
	public void addWaypoints(int [][] wps) {
		for(int i=0; i<wps.length; i++) {
			waypoints.add(new Coord((float)wps[i][0], (float)wps[i][1]));
		}
	}
	
	/**
	 * Checks if the sprite's head is at the next waypoint.
	 */
	public boolean atNextWaypoint() {
		
		Coord headCoordinate = sprite.getHeadPosition();
		if(headCoordinate.x == nextWPCoordinate.x && headCoordinate.y == nextWPCoordinate.y) {
			
			// Garbage collection
			headCoordinate = null;
			
			return true;
		}
		
		// Garbage Collection
		headCoordinate = null;
		
		return false;
	}
	
	/**
	 * Determines if the NPC is at the waypoint (given the index).
	 */
	public boolean atWaypoint(int wpIndex) {
		
		Coord headCoordinate = sprite.getHeadPosition();
		Coord wp = waypoints.get(wpIndex);
		//Gdx.app.log("DIRVEN", "wpIndex="+wpIndex);
		
		if(headCoordinate == null || wp == null) {
			return false;
		}
		
		// If at the npc's coordinates is the same as
		// the next waypoint's coordinates
		if(headCoordinate.x == wp.x && headCoordinate.y == wp.y) {
			// Garbage collection
			headCoordinate = null;
			
			// Return true, since they are at the same waypoint.
			return true;
		}
		
		// Garbage Collection
		headCoordinate = null;
		
		// Return false, 
		return false;
	} // END atWaypoint() method
	
	/**
	 * Reverse the direction of movement along the path.
	 */
	private void reverseDirection() {
		// If reversing direction is disabled
		if (reverseDirection == false) {
			// Don't do anything.
			return;
		}
		
		// If direction is forward
		if(directionForward) {
			// Switch to backwards
			directionForward = false;
		} else {
			// If direction is backwards
			// switch to forwards.
			directionForward = true;
		}
		
		sprite.reverseDirection();
	} // END reverseDirection() method
	
	/**
	 * Determines the next waypoint to move to.
	 */
	private void setNextWaypoint() {
		// Set the current waypoint to the previous one
		// since we are going to change it right after.
		previousWP = nextWP;
		
		// Get the next waypoint
		// If the direction is moving forward, increment waypoint index.
		// If the direction is movign backwards, decrement waypoint index.
		if(directionForward) {
			nextWP++;
		} else {
			nextWP--;
		}
		
		// If past last waypoint in terms of index
		if(nextWP >= waypoints.size()) {
			// set the next waypoint to the last one.
			nextWP = waypoints.size() - 1;
			// Reserse the direction for next time.
			reverseDirection();
			
		// If past first waypoint in terms of index
		} else if(nextWP < 0) {
			// Set the next waypoint to the first one
			nextWP = 0;
			// Reverse the direction for next time.
			reverseDirection();
		}
		
		nextWPCoordinate = waypoints.get(nextWP);
	} // END setNextWaypoint() method.
	
	/**
	 * Moves the sprite's head to the next waypoint along the path.
	 */
	public void moveAlongPath() {
		
		if(isFinishedMovingAlongPath()) {
			return;
		}
		
		// If the next waypiont hasn't been set, set it.
		if(nextWPCoordinate == null) {
			//Gdx.app.log("DIRVEN", "next wp not set. setting it");
			// Set the next waypoint to go to.
			setNextWaypoint();
			
		}
		
		// If NPC reached the next waypoint
		if(atNextWaypoint()) {
			//Gdx.app.log("DIRVEN", "reached next wp");
			// Set the next waypoint
			setNextWaypoint();
		}
		// Move to the next waypoint
		
		//Gdx.app.log("DIRVEN", "going to ("+nextWPCoordinate.x+", "+nextWPCoordinate.y+")");
		
		//Gdx.app.log("DIRVEN", "waypoints.size()="+waypoints.size());
		if(atWaypoint(waypoints.size()-1) && reverseDirection == false) {
			// If reverse direction is false and at last wp, dont do anything.
			//System.out.println ("at last waypoint");
			isFinishedMovingAlongPath = true;
		} else {
			// If reverse direction is true and not at last wp, dont do anything.
			/*String dir = "";
			switch(sprite.getDirection()) {
				case GridSprite.DIRECTION_NORTH: dir = "north"; break;
				case GridSprite.DIRECTION_SOUTH: dir = "south"; break;
				case GridSprite.DIRECTION_EAST: dir = "east"; break;
				case GridSprite.DIRECTION_WEST: dir = "west"; break;
			}
			Gdx.app.log("DIRVEN", "moving to ("+nextWPCoordinate.x+", "+nextWPCoordinate.y+") direction = "+dir);*/
			sprite.moveToCoordinate( nextWPCoordinate );
		}
	} // END moveAlongPath() method.
	
	/**
	 * Clears the waypoints in the path.
	 */
	public void destroy() {
		waypoints.clear();
	}

	/**
	 * @return the isFinishedMovingAlongPath
	 */
	public boolean isFinishedMovingAlongPath() {
		return isFinishedMovingAlongPath;
	}
	
	public void finalize() {
		waypoints.clear();
		waypoints = null;
		nextWPCoordinate = null;
		sprite = null;
		
		try {
			super.finalize();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
} // END Path class

